
#import "WorldSpaceView.h"
#import "SpaceView.h"
#import "Thinker.h"
#import <appkit/NXImage.h>
#import <math.h>
#import <libc.h>
#import <dpsclient/wraps.h>
#import <appkit/Panel.h>
#import <c.h>
#import <defaults/defaults.h>
#import <sys/file.h>

@implementation WorldSpaceView

// x & y periods in milliseconds
#define X_PERIOD 20000.0
#define Y_PERIOD 19000.0
#define DEFAULT_FPS 20

#define PI 3.1415926535

// #define MAX_IMAGE_WIDTH 200
// #define MAX_IMAGE_HEIGHT 150
#define MAX_IMAGE_WIDTH 256
#define MAX_IMAGE_HEIGHT 256

#define MAX_X_SPEED (26)
#define MAX_Y_SPEED (26)

#define BUFFER_WIDTH (MAX_IMAGE_WIDTH + MAX_X_SPEED + 1)
#define BUFFER_HEIGHT (MAX_IMAGE_HEIGHT + MAX_Y_SPEED + 1)



// move the world to its new position
- oneStep
{
	NXRect black = {0,0,0,0};
	NXRect ballRect;
	BRECT new;
	
	then = now;
	now = currentTimeInMs();
	// now += 30;

	// calculate new ball x & y position
	xpos = ((1 + sin(((float)now) / X_PERIOD * 2. * PI))/2.0) 
		* maxCoord.x;
	ypos = ((1 + sin(((float)now) / Y_PERIOD * 2. * PI))/2.0) 
		* maxCoord.y;
	
	
	if (xpos < (old.l - MAX_X_SPEED)) xpos = old.l - MAX_X_SPEED;
	else if (xpos > (old.l + MAX_X_SPEED)) xpos = old.l + MAX_X_SPEED;

	if (ypos < (old.b - MAX_Y_SPEED)) ypos = old.b - MAX_Y_SPEED;
	else if (ypos > (old.b + MAX_Y_SPEED)) ypos = old.b + MAX_Y_SPEED;


	/* animate by selecting a new image to blit */

	[self incrementImageNumber];
	
	new.l = floor(xpos);
	new.b = floor(ypos);
	new.r = new.l + imageSize.width;
	new.t = new.b + imageSize.height;
	
	ballRect.origin.x = 0;
	ballRect.origin.y = 0;
	ballRect.size.width = imageSize.width;
	ballRect.size.height = imageSize.height;
	
	redrawTo.x = MIN(new.l, old.l);
	redrawTo.y = MIN(new.b, old.b);

	redraw.origin.x = 0;
	redraw.origin.y = 0;
	redraw.size.width = (MAX(new.r, old.r)) - redrawTo.x + 1;
	redraw.size.height = (MAX(new.t, old.t)) - redrawTo.y + 1;
	
	black.size= redraw.size;

	[buffer lockFocus];
	PSsetgray(0);
	NXRectFill(&black);
	
	ballTo.x = new.l - redrawTo.x;
	ballTo.y = new.b - redrawTo.y;

	[currentImage composite:NX_SOVER fromRect:&ballRect toPoint:&ballTo];
	[buffer unlockFocus];
	
	
	// Now bring it onto the screen
	
	[buffer composite:NX_COPY fromRect:&redraw toPoint:&redrawTo];

	old = new;

	// now put in the stars...
	
	avoid.origin = redrawTo;
	[mySpaceView setVoidRect:&avoid];
	[mySpaceView oneStep];

	return self;
}



static char *basename(char *path, const char *suffix)
{
	char *begin, *end, *name;
	int len;
	
	if (strlen(path) < strlen(suffix)) {
		end = begin = path;
	}
	else {
	begin = rindex(path, '/');
	if (begin) begin++;
		else begin = path;
	
	end = path+strlen(path)-strlen(suffix);
	if (0 != strcmp(end, suffix))
		end = path+strlen(path);
	
	if (end < begin) end = begin;
	}
	len = end-begin;
	name = malloc(len+1);
	strncpy(name, begin, len);
	name[len] = '\0';
	return name;
}


static BOOL noAnimFile = FALSE;

- initFrame:(const NXRect *)frameRect
{
	char moduleDirectory[1024];
	const char *animSpeed, *animFile, *animName;
	char animFrame[1024];
	int i, f;
	id local_image;

	NXRect black = {0, 0, BUFFER_WIDTH, BUFFER_HEIGHT };

	[super initFrame:frameRect];
	[self allocateGState];		// For faster lock/unlockFocus
	[self setClipping:NO];		// even faster...

	//in this case, I only need one buffer for several Views
	if (!(buffer = [NXImage findImageNamed:"worldBuffer"]))
	{
		buffer = [[NXImage alloc] initSize:&black.size];
		[buffer setName:"worldBuffer"];
	}
	
	if ([buffer lockFocus])
	{
		PSsetgray(0);
		NXRectFill(&black);
		[buffer unlockFocus];
	}


	animSpeed = NXGetDefaultValue([NXApp appName], "animSpeed");
	if (animSpeed == NULL) framesPerSecond = DEFAULT_FPS;
	else framesPerSecond = atoi(animSpeed);

	animFile = NXGetDefaultValue([NXApp appName], "animFile");
	if (animFile == NULL)
	{
		sprintf(moduleDirectory,"%s/Globe.anim",
			[(BSThinker()) moduleDirectory:"WorldSpace"]);
		animFile = moduleDirectory;
	}
	animName = basename((char *)animFile, ".anim");
   
	imageList = [[List alloc] init];

	/* construct the image list */
	for (i=0; ; i++)
	{
		sprintf(animFrame, "%s/%s.%d.tiff", animFile, animName, i+1);
		if (!(local_image = [NXImage findImageNamed:animFrame]))
		{
			if ((f=open(animFrame, O_RDONLY)) < 0) break;
			close(f);

			local_image = [[NXImage alloc] initFromFile:animFrame];
			if (local_image == NULL) break;	// never null, even if no file
			[local_image setName:animFrame];
		}

		[imageList addObject:local_image];
		if (i == 0) [local_image getSize: &imageSize];
	}
	numberOfFrames = i;
	currentFrame = 0;
	
	if (numberOfFrames == 0)
	{
		if (!noAnimFile)
		NXRunAlertPanel([NXApp appName], "Could not open %s",
				NULL, NULL, NULL, animFile);
		noAnimFile = TRUE;
		return nil;
	}

	nextRotationTime = 0;

	[self newViewSize];

	mySpaceView = [[SpaceView alloc] initFrame:frameRect];
	avoid.size = imageSize;

	return self;
}

- sizeTo:(NXCoord)width :(NXCoord)height
{
	[super sizeTo:width :height];
	[mySpaceView sizeTo:width :height];
	[self newViewSize];
	return self;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
	// if (!rects || !rectCount) return self;
	
	//PSsetgray(0);
	//NXRectFill(rects);
	
	//NXRectClip(rects);

	return self;
}

- newViewSize
{
	//this is called every time View size changes
	NXRect black = {0, 0, BUFFER_WIDTH, BUFFER_HEIGHT };

	then = now = currentTimeInMs();

	if (oldSize.width == bounds.size.width &&
			oldSize.height == bounds.size.height)
		return self;
	else
	{
		oldSize.width = bounds.size.width;
		oldSize.height = bounds.size.height;
	}
	
	maxCoord.x = bounds.size.width - imageSize.width;
	maxCoord.y = bounds.size.height - imageSize.height;
	if (maxCoord.x < 0) maxCoord.x = 0;
	if (maxCoord.y < 0) maxCoord.y = 0;


	old.l = old.r = maxCoord.x/2;
	old.b = old.t = maxCoord.y/2;
	ballTo.x = ballTo.y = 0;

	if ([buffer lockFocus])
	{
		PSsetgray(0);
		NXRectFill(&black);
		[buffer unlockFocus];
	}

	return self;
}

- incrementImageNumber
{
	if (now > nextRotationTime)
	{
		if (++currentFrame >= numberOfFrames) currentFrame = 0;
		currentImage = [imageList objectAt:currentFrame];
		nextRotationTime = now + 1000/framesPerSecond;
	}

	return self;
}

- (const char *)windowTitle
{
	return "World in Space";
}

- didLockFocus
{
	[mySpaceView didLockFocus];
	return self;
}

- inspector:sender
{
    char buf[MAXPATHLEN];
	
    if (!sharedInspectorPanel)
	{
		sprintf(buf,"%s/WorldSpace.nib",[sender moduleDirectory:"WorldSpace"]);
		[NXApp loadNibFile:buf owner:self withNames:NO];
    }
    return sharedInspectorPanel;
}

@end
